﻿using OpenCvSharp;
using RemoteSensingImageDirectServer.Data;
using RemoteSensingImageDirectServer.Models.Map.Cache;
using RemoteSensingImageDirectServer.Untils.Source;
using System.Collections.Concurrent;
using System.Collections.Immutable;
using RemoteSensingImageDirectServer.Service.LRU;
namespace RemoteSensingImageDirectServer.Untils.LRU
{
    public class RasterMapLink : IRasterMapLink
    {
        /// <summary>
        /// 资源是否被释放
        /// </summary>
        private bool m_disposed;

        /// <summary>
        /// 私有静态锁对象，减少锁的范围
        /// </summary>
        private readonly object syncRoot = new();

        /// <summary>
        /// 线程安全的表
        /// </summary>
        private readonly ImmutableSortedDictionary<string, IRasterMapNode> _mapPairs;

        /// <summary>
        /// 服务池
        /// </summary>
        private readonly IServiceProvider _serviceProvider;

        /// <summary>
        /// 表的最大值
        /// </summary>
        public readonly int MapMaxSize;


        /// <summary>
        /// RasterMapLink的构造函数，用Service进行托管
        /// </summary>
        /// <param name="_configuration">Service托管的配置文件类</param>
        public RasterMapLink(IConfiguration configuration, IServiceProvider serviceProvider)
        {
            this._mapPairs = ImmutableSortedDictionary<string, IRasterMapNode>.Empty;
            this.MapMaxSize = configuration.GetValue<int>("LRU:RasterMapNodeMaxSize");
            this._serviceProvider = serviceProvider;
        }

        /// <summary>
        /// 获取图源节点
        /// </summary>
        /// <param name="key">图源节点关键词</param>
        /// <returns></returns>
        /// <exception cref="NotImplementedException"></exception>
        public IRasterMapNode? Get(string key)
        {
            lock (syncRoot)
            {
                if (!_mapPairs.TryGetValue(key, out IRasterMapNode? node))
                {
                    return null;
                }
                RefreshNode(node);
                return node;
            }
        }

        /// <summary>
        /// 创建图源节点并返回图源节点
        /// </summary>
        /// <param name="key">图源节点关键词</param>
        /// <param name="filename">数据源路径</param>
        /// <param name="maskfilename">掩膜文件路径</param>
        /// <param name="epsg">投影id</param>
        /// <returns></returns>
        public IRasterMapNode Put(string key, string filename, string maskfilename, int epsg)
        {
            lock (syncRoot)
            {
                if (!this._mapPairs.TryGetValue(key, out IRasterMapNode? node))
                {
                    if (this._mapPairs.Count > this.MapMaxSize)
                    {
                        var lastpair = this._mapPairs.Last();
                        RemoveNode(lastpair.Key, true);
                    }
                    using (var scope = _serviceProvider.CreateScope())
                    {
                        using (var dbContext = scope.ServiceProvider.GetRequiredService<RasterMapCacheContext>())
                        {
                            var item = dbContext.TBSingleImages.SingleOrDefault(q => q.Key == key);
                            if (item == null)
                            {
                                node = new RasterMapNode(key, filename, maskfilename, epsg);
                                AddSingleImage(key, filename, epsg, node);
                            }
                            else
                            {
                                node = new RasterMapNode(key, filename, item.VrtData, item.WarpData, maskfilename, epsg);
                            }
                            if (!_mapPairs.ContainsKey(key))
                            {
                                AddNode(node);
                            }
                            return node;
                        }
                    }
                }
                else
                {
                    RefreshNode(node);
                    return node;
                }
            }
        }

        /// <summary>
        /// 创建图源节点并返回图源节点
        /// </summary>
        /// <param name="key">图源节点关键词</param>
        /// <returns></returns>
        public IRasterMapNode Put(string key)
        {
            lock (syncRoot)
            {
                if (!this._mapPairs.TryGetValue(key, out IRasterMapNode? node))
                {
                    if (this._mapPairs.Count > this.MapMaxSize)
                    {
                        var lastpair = this._mapPairs.Last();
                        RemoveNode(lastpair.Key, true);
                    }
                    using (var scope = _serviceProvider.CreateScope())
                    {
                        using (var dbContext = scope.ServiceProvider.GetRequiredService<RasterMapCacheContext>())
                        {
                            var item = dbContext.TBGroupImages.SingleOrDefault(q => q.Key == key);
                            if (item != null)
                            {
                                node = new RasterMapNode(key, item);
                            }

                            if (!_mapPairs.ContainsKey(key))
                            {
                                AddNode(node);
                            }
                            return node;
                        }
                    }
                }
                else
                {
                    RefreshNode(node);
                    return node;
                }
            }
        }

        /// <summary>
        /// 移除节点
        /// </summary>
        /// <param name="key">图源节点关键词</param>
        public void Remove(string key)
        {
            lock (syncRoot)
            {
                RemoveNode(key, true);
            }
        }

        /// <summary>
        /// 添加节点到数据库
        /// </summary>
        /// <param name="key"></param>
        /// <param name="filename"></param>
        /// <param name="epsg"></param>
        /// <param name="node"></param>
        private void AddSingleImage(string key, string filename, int epsg, IRasterMapNode node)
        {
            using (var scope = _serviceProvider.CreateScope())
            {
                using (var dbContext = scope.ServiceProvider.GetRequiredService<RasterMapCacheContext>())
                {
                    var source = (IRSISingleImageSource)node.GetMapSource();
                    var singleImage = new SingleImageCache()
                    {
                        Key = key,
                        Filename = filename,
                        EPSG = epsg,
                        VrtData = source.GetVrtBytes(),
                        WarpData = source.GetWarpBytes()
                    };
                    dbContext.TBSingleImages.AddAsync(singleImage);
                    dbContext.SaveChangesAsync();
                }
            }
        }


        private void RefreshNode(IRasterMapNode node)
        {
            RemoveNode(node.GetKey(), false);
            AddNode(node);
        }

        private void AddNode(IRasterMapNode node)
        {
            this._mapPairs.Add(node.GetKey(), node);
        }

        private void RemoveNode(string key, bool disposing)
        {
            if (disposing)
            {
                if (this._mapPairs.Remove(key, out IRasterMapNode? rNode))
                {
                    rNode?.Dispose();
                }
            }
            else
            {
                if (this._mapPairs.Remove(key, out IRasterMapNode? rNode))
                {
                }
            }
        }

        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        protected virtual void Dispose(bool disposing)
        {
            if (!this.m_disposed)
            {
                if (disposing)
                {
                    foreach (var pair in this._mapPairs)
                    {
                        pair.Value.Dispose();
                    }

                    this._mapPairs.Clear();
                }

                this.m_disposed = true;
            }
            else
            {
                throw new Exception("资源已被释放");
            }
        }
    }
}
